﻿using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
using WHLRDF.Application.BLL;
using WHLRDF.Application.Model;
using WHLRDF.Cache;
using WHLRDF.Install.Model;
using WHLRDF.Log;
using WHLRDF.ORM;
using WHLRDF.ORM.EF;

namespace WHLRDF.Install.BLL
{
    public class InstallService : SerivceBase, IInstallService
    {
        /// <summary>
        /// 生成以及读取初始化数据路径
        /// </summary>
        private string INITPATH
        {
            get { return "/" + ApplicationEnvironments.Site.ConfigPath.Where(x => x.RootKey == ConfigPathKeyType.config).FirstOrDefault().Path + "/initdata/"; }
        }
        /// <summary>
        /// 校验安装环境
        /// </summary>
        /// <param name="site"></param>
        /// <param name="smtp"></param>
        /// <returns></returns>
        public List<TaskModel> CheckInstallEnv(SiteSettings site, SMTPConfig smtp)
        {
            List<TaskModel> lstResult = new List<TaskModel>();
            string strError = "";

            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-验证-DB", "", (task) =>
            {
                return CheckInstallDb(site, ref strError);
            }, "db"));

            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-验证-Redis", "", (task) =>
            {
                return CheckInstallRedis(site, ref strError);
            }, "redis"));
            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-验证-文件路径", "", (task) =>
            {
                return CheckRootPath(site, ref strError);
            }, "path"));
            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-验证-邮件服务器", "", (task) =>
            {
                return CheckSMTP(smtp, ref strError);
            }, "smtp"));
            return lstResult;
        }

        #region 系统检测
        /// <summary>
        /// 检测Db是否存在
        /// </summary>
        /// <param name="site"></param>
        /// <returns></returns>
        private bool CheckInstallDb(SiteSettings site, ref string strError)
        {
            if (string.IsNullOrWhiteSpace(site.ConnectionString))
            {
                strError = "请输入数据库链接字符串";
                return false;
            }
            var dbtype = Type.GetType($"WHLRDF.ORM.{site.ProviderType}.AdoDataAccess,WHLRDF.ORM.{site.ProviderType}");
            try
            {
                using (AdoRepository adoRepository = new AdoRepository(dbtype, site.ConnectionString, site.Version))
                {
                    LigerGrid ligerGrid = new LigerGrid();
                    ligerGrid.sortOrder = "desc";
                    ligerGrid.sortName = EntityBase.__CreateDate;
                    ligerGrid = adoRepository.GetSource("ORG_User", ligerGrid);
                    strError = "数据库已存在，如需升级请手动更新数据库";

                }
            }
            catch
            {
                strError = "数据库不存在,需要创建数据库";
            }
            return true;
        }

        /// <summary>
        /// 检查redis
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool CheckInstallRedis(SiteSettings site, ref string strError)
        {
            try
            {
                //if (site.CacheType!=CacheType.Redis)
                //{
                //    strError = "不启用Redis 无需验证";
                //    return true;
                //}
                if (string.IsNullOrWhiteSpace(site.RedisConnection))
                {
                    strError = "请输入redis链接字符串";
                    return true;
                }
                using (RedisService redisService = new RedisService(site.RedisConnection))
                {
                    redisService.Add("text-Connection", "1");
                }
                strError = "redis服务器正常";
            }
            catch
            {
                strError = "redis链接异常，请检查服务器！";
                return false;
            }
            return true;
        }

        /// <summary>
        /// 检测路径
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool CheckRootPath(SiteSettings site, ref string strError)
        {
            bool flag = true;
            if (string.IsNullOrWhiteSpace(site.DefaultDirectory))
            {
                strError += "\r\n系统默认目录不允许为空";
                flag = false;
            }
            if (string.IsNullOrWhiteSpace(site.BaseDirectory))
            {
                strError += "\r\n系统资源目录不允许为空";
                flag = false;
            }

            if (!flag)
            {
                return false;
            }
            if (!Directory.Exists(site.DefaultDirectory))
            {
                strError += "\r\n系统默认目录不存在需要创建";
            }
            if (!Directory.Exists(site.BaseDirectory))
            {
                strError += "\r\n系统资源目录不存在需要创建";
            }

            return flag;
        }

        /// <summary>
        /// 检测邮箱服务器
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool CheckSMTP(SMTPConfig smtp, ref string strError)
        {
            bool flag = true;
            if (string.IsNullOrWhiteSpace(smtp.Server))
            {
                strError += "邮件服务器不能为空";
                flag = false;
            }
            if (smtp.Port <= 0)
            {
                strError += "端口号必填并且不能小于0";
                flag = false;
            }
            if (string.IsNullOrWhiteSpace(smtp.Account))
            {
                strError += "邮件账号不能为空";
                flag = false;
            }
            if (string.IsNullOrWhiteSpace(smtp.Password))
            {
                strError += "邮件密码不能为空";
                flag = false;
            }
            if (string.IsNullOrWhiteSpace(smtp.SendAddress))
            {
                strError += "邮件发送人不能为空";
                flag = false;
            }
            try
            {

                var mail = new SmtpHelper(smtp.Server, smtp.Port, smtp.Account, smtp.Password);
                if (mail.Send(smtp.SendAddress, smtp.SendAddress, "测试激活邮件", "测试激活邮件", ref strError))
                {
                    strError = "邮件配置成功";
                }

            }
            catch (Exception ex)
            {
                strError = "邮件配置不正确，请检查邮件设置！" + ex.StackTrace;
                flag = false;
            }

            return flag;
        }
        #endregion

        #region install 系统安装

        /// <summary>
        /// 安装数据库
        /// </summary>
        /// <param name="site"></param>
        /// <param name="smtp"></param>
        /// <returns></returns>
        public TaskModel InstallDbEnv(SiteSettings site, SMTPConfig smtp)
        {
            string strError = "";
            return TaskHelper.Instance.TaskStart("系统安装-安装-数据库", "", (task) =>
            {
                var flag = InstallDb(site, smtp, ref strError);
                if (!flag)
                {
                    task.Message = strError;

                }
                return flag;
            }, "db");

        }
        public List<TaskModel> InitialEnv(SiteSettings site, SMTPConfig smtp)
        {
            SaveSetting(site, smtp);
            List<TaskModel> lstResult = new List<TaskModel>();
            string strError = "";
            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-写入-初始化文件路径", "", (task) =>
            {
                return InstallRootPath(site, ref strError);
            }, "path"));
            lstResult.Add(TaskHelper.Instance.TaskStart("系统安装-写入-初始化数据", "", (task) =>
            {
                return InstallData(site, task, ref strError);
            }, "data"));
            return lstResult;
        }
        /// <summary>
        /// 创建数据库
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool InstallDb(SiteSettings site, SMTPConfig smtp, ref string strError)
        {
            //var dbtype = Type.GetType($"WHLRDF.ORM.{site.ProviderType}.EF{EFDbType.Install.ToString()}DataAccess,WHLRDF.ORM.{site.ProviderType}");
            using (InstallDbContext dbContext = new InstallDbContext(site.ProviderType, site.ConnectionString, ApplicationEnvironments.Site.Components.Plug))
            {
                try
                {
                    if (dbContext.DbEnsureCreated())
                    {
                        return true;
                    }
                    else {
                        strError = "数据库已存在，无法创建！";
                    }
                    return true;

                }
                catch (Exception ex)
                {
                    strError = "系统数据库创建失败，原因：" + ex.Message;
                }
                return false;
            }
        }

        /// <summary>
        /// 初始化数据
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool InstallData(SiteSettings site, TaskModel task, ref string strError)
        {
            string path = site.BaseDirectory + INITPATH+ "initdata.json";
            FileInfo info = new FileInfo(path);
            if (!info.Exists)
            {
                strError += "初始化文件路径不存在，无需初始化数据！";
                return true;
            }
            string sqlPath = site.BaseDirectory + INITPATH + "/update-sql/" + site.ProviderType.ToString().ToLower();
            string jsonData = FileHelper.ReadLine(path);
            if (string.IsNullOrWhiteSpace(jsonData))
            {
                strError += "初始化文件内容不存在，无需初始化！";
                return true;
            }
            var dbtype = Type.GetType($"WHLRDF.ORM.{site.ProviderType}.AdoDataAccess,WHLRDF.ORM.{site.ProviderType}");
            Dictionary<string, string> dicResult = new Dictionary<string, string>();

            lock (this)
            {
                using (var ado = new AdoRepository(dbtype, site.ConnectionString, site.Version))
                {
                    if (!ExcuteUpdateSql(sqlPath, ado, ref strError))
                    {
                        return false;
                    }

                    var lstPage = SystemMenuHelper.InitMenu(ApplicationEnvironments.Site.Components.Plug);
                    ado.BatchSave<PageEntity>(lstPage);

                    Dictionary<string, object> initalData = JSONHelper.FromJson<Dictionary<string, object>>(jsonData);
                    if (ApplicationEnvironments.Site.Components.Plug != null && ApplicationEnvironments.Site.Components.Plug.Count > 0)
                    {
                        foreach (var components in ApplicationEnvironments.Site.Components.Plug)
                        {
                            if (!components.Opened)
                            {
                                continue;
                            }
                            if (components.Service != null && components.Service.Length > 0)
                            {
                                foreach (var item in components.Service)
                                {
                                    bool isFrist = false;
                                    string strResult = "";
                                    if (string.IsNullOrEmpty(components.Id))
                                    {
                                        isFrist = true;
                                    }
                                    Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(AppDomain.CurrentDomain.BaseDirectory + item);
                                    var initializeType = assembly.GetTypes().Where(s => s.GetInterfaces().Contains(typeof(IInitializeService))).FirstOrDefault();
                                    if (initializeType == null)
                                    {
                                        continue;
                                    }
                                    var initializeService = ReflectionHelper.CreateInstance(initializeType);
                                    if (initializeService != null)
                                    {
                                        if (initializeService != null && initializeService is IInitializeService)
                                        {
                                            task.Message = item + "数据初始化";
                                            if (!(initializeService as IInitializeService).Write(ado, initalData, ref strResult))
                                            {
                                                dicResult.Add(components.Name, "数据初始化失败，原因:" + strResult);
                                                if (isFrist)
                                                {
                                                    return false;
                                                }
                                            }
                                            else
                                            {
                                                dicResult.Add(components.Name, "数据初始化成功");
                                            }
                                        }

                                    }

                                }

                            }

                        }

                    }
                    return true;
                }
            }
        }

        /// <summary>
        /// 执行更新语句
        /// </summary>
        /// <param name="sqlPath"></param>
        /// <param name="adoRepository"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool ExcuteUpdateSql(string sqlPath,AdoRepository adoRepository,ref string strError)
        {
            
            if (FileHelper.DirExist(sqlPath))
            {
                List<string> fileNames = new List<string>();
                var arrsqlFiles= FileHelper.GetFiles(sqlPath,"*.sql");
                if (arrsqlFiles != null && arrsqlFiles.Length > 0)
                {
                    foreach (var item in arrsqlFiles)
                    {
                        fileNames.Add(item.Name);
                    }
                    fileNames.Sort();
                    foreach (var fileName in fileNames)
                    {
                        string sql = FileHelper.ReadLine(sqlPath+"/"+fileName);
                        if (!string.IsNullOrWhiteSpace(sql))
                        {
                            adoRepository.Execute(sql);
                        }
                    }
                }
               
              
            }
            return true;
        }
        /// <summary>
        /// 安装路径
        /// </summary>
        /// <param name="site"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        private bool InstallRootPath(SiteSettings site, ref string strError)
        {
           
            if (!Directory.Exists(site.DefaultDirectory))
            {
                Directory.CreateDirectory(site.DefaultDirectory);
                strError += "\r\n系统默认目录创建成功";
            }
            else
            {
                strError += "\r\n系统默认目录已存在";
            }
            if (!Directory.Exists(site.BaseDirectory))
            {
                Directory.CreateDirectory(site.BaseDirectory);
                strError += "\r\n系统资源目录不存在需要创建";
            }
            else
            {
                strError += "\r\n系统资源目录已存在";
            }

            return true;
        }

        /// <summary>
        /// 查询任务是否在进行中
        /// </summary>
        /// <param name="taskId">任务id</param>
        /// <returns></returns>
        public TaskModel CheckTaskStatus(string taskId, ref string strError)
        {
            var task= TaskHelper.Instance.GetById(taskId,ref strError);
          
            return task;
        }
        /// <summary>
        /// 查询任务是否在进行中
        /// </summary>
        /// <param name="taskId">任务id</param>
        /// <returns></returns>
        public bool TaskRemove(string taskId, ref string strError)
        {
            return TaskHelper.Instance.Remove(taskId, ref strError);
        }
        #endregion

        #region 创建初始化数据
        /// <summary>
        /// 生成插件初始化数据
        /// </summary>
        /// <param name="site">数据库配置</param>
        /// <param name="plugs">插件</param>
        /// <param name="strError">错误信息</param>
        /// <returns></returns>
        public TaskModel CreateIntallData(SiteSettings site, string plugs)
        {
            if (string.IsNullOrWhiteSpace(plugs))
            {
                return new TaskModel() {  IsOpen=false, IsSuccess=false,Message= "请输入需要初始化业务的类" };
            }
           return TaskHelper.Instance.TaskStart("系统-生成初始化数据", "", (task) =>
            {
                var lstplug = plugs.Trim().Split(new char[] { ',', ';' });
                string path = ApplicationEnvironments.BaseDirectory + INITPATH;
                Dictionary<string, object> initalData = new Dictionary<string, object>();
                var dbtype = Type.GetType($"WHLRDF.ORM.{site.ProviderType}.AdoDataAccess,WHLRDF.ORM.{site.ProviderType}");
                using (AdoRepository ado = new AdoRepository(dbtype, site.ConnectionString, site.Version))
                {
                    string message = "";
                    List<string> excute = new List<string>();
                    if (lstplug != null && lstplug.Length > 0)
                    {
                        foreach (var item in lstplug)
                        {
                            if (excute.Contains(item))
                            {
                                continue;
                            }
                            excute.Add(item);
                            Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(AppDomain.CurrentDomain.BaseDirectory + item);
                            var type = assembly.GetTypes().Where(s => s.GetInterfaces().Contains(typeof(IInitializeService))).FirstOrDefault();
                            if (type == null)
                            {
                                continue;
                            }
                            var initializeService = ReflectionHelper.CreateInstance(type);
                            if (initializeService != null && initializeService is IInitializeService)
                            {
                                (initializeService as IInitializeService).Read(ado, ref initalData, ref message);
                            }
                        }
                    }
                    if (initalData != null && initalData.Count > 0)
                    {
                        message = $"生成成功，请到目录下{path}检查文件格式是否正确";
                        FileHelper.WriteLine(path, initalData.ToJson());
                    }
                    return true;
                }
            }, "data");
          
        }
        #endregion

        public bool SaveSetting(SiteSettings site, SMTPConfig smtp)
        {
            string contentPath = Environment.CurrentDirectory+ @"\"; ;   //项目根目录
            var filePath = contentPath + "appsettings.json";
            JObject jsonObject;
            using (StreamReader file = new StreamReader(filePath))
            using (JsonTextReader reader = new JsonTextReader(file))
            {
                jsonObject = (JObject)JToken.ReadFrom(reader);
                jsonObject["SiteSettings"][nameof(site.ApplicationName)] = site.ApplicationName;
                jsonObject["SiteSettings"][nameof(site.Version)] = site.Version;
                jsonObject["SiteSettings"][nameof(site.DBProviderType)] = site.DBProviderType.ToString();
                jsonObject["SiteSettings"][nameof(site.AdminUser)] = site.AdminUser;
                jsonObject["SiteSettings"][nameof(site.ConnectionString)] = site.ConnectionString;
                jsonObject["SiteSettings"][nameof(site.DefaultDirectory)] = site.DefaultDirectory;
                jsonObject["SiteSettings"][nameof(site.BaseDirectory)] = site.BaseDirectory;
                jsonObject["SiteSettings"][nameof(site.ORMType)] = site.ORMType.ToString();
                jsonObject["SiteSettings"][nameof(site.ServerAddress)] = site.ServerAddress;
                jsonObject["SiteSettings"][nameof(site.AESKey)] = site.AESKey;
                jsonObject["SiteSettings"][nameof(site.RedisConnection)] = site.RedisConnection;
                jsonObject["SiteSettings"][nameof(site.MD5key)] = site.MD5key;
                jsonObject["SiteSettings"][nameof(site.IsSignalR)] = site.IsSignalR;
                jsonObject["SiteSettings"][nameof(site.AESIV)] = site.AESIV;
                jsonObject["SiteSettings"][nameof(site.SessionTimeout)] = site.SessionTimeout;
                jsonObject["SiteSettings"][nameof(site.IsEntityCache)] = site.IsEntityCache;
                jsonObject["SiteSettings"][nameof(site.CacheType)] = site.CacheType.ToString();
                jsonObject["SiteSettings"]["SMTP"][nameof(smtp.Server)] = smtp.Server;
                jsonObject["SiteSettings"]["SMTP"][nameof(smtp.Port)] = smtp.Port;
                jsonObject["SiteSettings"]["SMTP"][nameof(smtp.Account)] = smtp.Account;
                jsonObject["SiteSettings"]["SMTP"][nameof(smtp.Password)] = smtp.Password;
                jsonObject["SiteSettings"]["SMTP"][nameof(smtp.SendAddress)] = smtp.SendAddress;
            }

            using (var writer = new StreamWriter(filePath))
            {
                using (JsonTextWriter jsonwriter = new JsonTextWriter(writer))
                {
                    jsonObject.WriteTo(jsonwriter);
                }
            }
            return true;

        }
    }
}
